#include "modules/rtp_rtcp/rtcp_sender.h"
#include <rtc_base/logging.h>
#include <modules/rtp_rtcp/source/rtcp_packet/sender_report.h>
#include <modules/rtp_rtcp/source/rtcp_packet/receiver_report.h>
#include <modules/rtp_rtcp/source/rtcp_packet/extended_reports.h>

#include <modules/rtp_rtcp/source/rtcp_packet/nack.h>
#include <modules/rtp_rtcp/source/rtcp_packet/pli.h>
#include <modules/rtp_rtcp/source/rtp_rtcp_config.h>
#include <modules/rtp_rtcp/source/time_util.h>
#include <system_wrappers/include/clock.h>

namespace xrtc
{
    namespace
    {
        const int kDefaultAudioReportInterval = 5000;
        const int kDefaultVideoReportInterval = 1000;
        const int kRtcpAnyExtendedReports = webrtc::kRtcpXrReceiverReferenceTime |
                                            webrtc::kRtcpXrDlrrReportBlock;
    } // namespace
    class RTCPSender::RtcpContext
    {
    public:
        RtcpContext(const FeedbackState &feedback_state,
                    int32_t nack_size, const uint16_t *nack_list,
                    webrtc::Timestamp now) : feedback_state_(feedback_state),
                                             nack_size_(nack_size),
                                             nack_list_(nack_list),
                                             now_(now) {}

        const FeedbackState &feedback_state_;
        int32_t nack_size_;
        const uint16_t *nack_list_;
        webrtc::Timestamp now_;
    };

    class RTCPSender::PacketSender
    {
    public:
        PacketSender(size_t max_packet_size, webrtc::rtcp::RtcpPacket::PacketReadyCallback callback) : max_packet_size_(max_packet_size), callback_(callback)
        {
        }
        ~PacketSender() {}
        void AppendPacket(const webrtc::rtcp::RtcpPacket &packet)
        {
            packet.Create(buffer_, &index_, max_packet_size_, callback_);
        }
        void Send()
        {

            if (index_ > 0)
            {
                callback_(rtc::ArrayView<const uint8_t>(buffer_, index_));
                index_ = 0;
            }
        }

    private:
        size_t max_packet_size_; // 真实控制packet的大小
        webrtc::rtcp::RtcpPacket::PacketReadyCallback callback_;
        size_t index_ = 0;
        uint8_t buffer_[IP_PACKET_SIZE];
    };
    RTCPSender::RTCPSender(const RtpRtcpConfig &config) : clock_(config.clock),
                                                          audio_(config.audio),
                                                          ssrc_(config.local_media_ssrc),
                                                          receive_stat_(config.receive_stat),
                                                          max_packet_size_(IP_PACKET_SIZE - 28), // 28 =>udp 的包头
                                                          report_interval_ms_(config.rtcp_report_interval_ms.value_or(
                                                              audio_ ? kDefaultAudioReportInterval : kDefaultVideoReportInterval)),
                                                          cur_report_interval_ms_(report_interval_ms_ / 2),
                                                          random_(clock_->TimeInMicroseconds()),
                                                          rtp_rtcp_module_observer_(config.rtp_rtcp_module_observer),
                                                          enable_xr_(config.enable_xr)
    {
        builders_[webrtc::kRtcpSr] = &RTCPSender::BuildSR;
        builders_[webrtc::kRtcpRr] = &RTCPSender::BuildRR;
        builders_[webrtc::kRtcpNack] = &RTCPSender::BuildNack;
        builders_[webrtc::kRtcpPli] = &RTCPSender::BuildPli;
        builders_[kRtcpAnyExtendedReports] = &RTCPSender::BuildXR;
    }
    RTCPSender::~RTCPSender()
    {
    }
    int RTCPSender::SendRTCP(const FeedbackState &feedback_state, webrtc::RTCPPacketType packet_type,
                             int32_t nack_size, const uint16_t *nack_list)
    {
        auto callback = [&](rtc::ArrayView<const uint8_t> packet)
        {
            //   RTC_LOG(LS_WARNING) << "===builder rtcp packet,size:" << packet.size();
            if (rtp_rtcp_module_observer_)
            {
                rtp_rtcp_module_observer_->OnLocalRtcpPacket(audio_ ? webrtc::MediaType::AUDIO : webrtc::MediaType::VIDEO,
                                                             packet.data(), packet.size());
            }
        };
        absl::optional<PacketSender> sender;
        sender.emplace(max_packet_size_, callback);

        auto result = ComputeCompoundRTCPPacket(feedback_state, nack_size, nack_list, packet_type, *sender);
        if (result)
        {
            // 打包出问题啦，直接返回结果s
            return *result;
        }
        sender->Send();
        return 0;
    }
    void RTCPSender::SetRtcpStatus(webrtc::RtcpMode method)
    {
        method_ = method;
    }
    void RTCPSender::SetFlag(uint32_t type, bool is_volatile)
    {
        report_flags_.insert(ReportFlag(type, is_volatile));
    }
    bool RTCPSender::IsFlagPresent(uint32_t type)
    {
        return (report_flags_.find(ReportFlag(type, false)) != report_flags_.end());
    }
    bool RTCPSender::ConsumeFlag(uint32_t type, bool force)
    {
        auto it = report_flags_.find(ReportFlag(type, false));
        if (it == report_flags_.end())
        {
            return false;
        }
        if (it->is_volatile || force)
        {
            report_flags_.erase(it);
        }
        return true;
    }

    absl::optional<uint32_t> RTCPSender::ComputeCompoundRTCPPacket(const FeedbackState &feedback_state,
                                                                   int32_t nack_size, const uint16_t *nack_list,
                                                                   webrtc::RTCPPacketType packet_type, PacketSender &sender)
    {
        if (method_ == webrtc::RtcpMode::kOff)
        {
            RTC_LOG(LS_WARNING) << "cannot send rtcp if it is disabled";
            return -1;
        }
        SetFlag(packet_type, true); // 设置成ture=>只发送一次
        RtcpContext context(feedback_state, nack_size, nack_list, clock_->CurrentTime());

        PrepareReport();

        // 遍历report flag 生成相应类xing的rtcp 包

        auto it = report_flags_.begin();
        while (it != report_flags_.end())
        {
            uint32_t rtcp_packet_type = it->type;
            if (it->is_volatile)
            {
                report_flags_.erase(it++);
            }
            else
            {
                ++it;
            }
            auto builder_it = builders_.find(rtcp_packet_type);
            if (builder_it == builders_.end())
            {
                RTC_LOG(LS_WARNING) << "Counld not build rtcp packet for packet type: " << it->type;
            }
            else
            {
                BuilderFunc func = builder_it->second;
                (this->*func)(context, sender);
            }
        }
        return absl::nullopt;
    }

    void RTCPSender::PrepareReport()
    {
        bool generate_report;
        if (IsFlagPresent(webrtc::kRtcpSr) || IsFlagPresent(webrtc::kRtcpRr))
        {
            generate_report = false;
        }
        else
        {
            generate_report = (ConsumeFlag(webrtc::kRtcpReport) && (method_ == webrtc::RtcpMode::kReducedSize)) ||
                              (method_ == webrtc::RtcpMode::kCompound);
            if (generate_report)
            {
                SetFlag(sending_ ? webrtc::kRtcpSr : webrtc::kRtcpRr, true);
            }
        }
        if (generate_report)
        {
            if (!sending_ && enable_xr_)
            {
                SetFlag(kRtcpAnyExtendedReports, true);
            }
        }
        uint32_t min_interval = report_interval_ms_;
        cur_report_interval_ms_ = random_.Rand(min_interval * 1 / 2, min_interval * 3 / 2);
    }
    std::vector<webrtc::rtcp::ReportBlock> RTCPSender::CreateRtcpReportBlocks(
        const FeedbackState &feedback_state)
    {
        std::vector<webrtc::rtcp::ReportBlock> result;
        if (!receive_stat_)
        {
            return result;
        }
        result = receive_stat_->RtcpReportBlocks(webrtc::RTCP_MAX_REPORT_BLOCKS);
        // 进一步设置lastSr和delaySinceLastSr
        if (!result.empty() && ((feedback_state.last_rr_ntp_secs > 0) || (feedback_state.last_rr_ntp_frac > 0)))
        {
            // 计算delay since last sr
            // 当前发送RR包，接收端压缩后32位NTP时间
            int32_t now = webrtc::CompactNtp(clock_->CurrentNtpTime());
            // 收到最近一次SR 包时，接受端压缩后32位NTP时间
            int32_t receive_time = feedback_state.last_rr_ntp_secs & 0x0000FFFF; // 取缔16位，高16 位舍去
            receive_time <<= 16;
            receive_time += ((feedback_state.last_rr_ntp_frac & 0xFFFF0000) >> 16);
            int32_t delay_since_last_sr = now - receive_time;
            for (auto &report_block : result)
            {
                report_block.SetLastSr(feedback_state.remote_sr);
                report_block.SetDelayLastSr(delay_since_last_sr);
            }
        }
        return result;
    }
    void RTCPSender::BuildSR(const RtcpContext &ctx, PacketSender &sender)
    {
        if (lastest_rtp_timestamp_ == 0 || !lastest_ntp_.Valid())
        {
            return;
        }
        webrtc::rtcp::SenderReport sr;
        sr.SetSenderSsrc(ssrc_);
        sr.SetRtpTimestamp(lastest_rtp_timestamp_);
        sr.SetNtp(lastest_ntp_);
        sr.SetPacketCount(ctx.feedback_state_.packets_sent);
        sr.SetOctetCount(ctx.feedback_state_.media_bytes_sent);
        sender.AppendPacket(sr);
    }
    void RTCPSender::BuildRR(const RtcpContext &ctx, PacketSender &sender)
    {
        // test
        // FeedbackState feedback_state;
        // test end
        webrtc::rtcp::ReceiverReport rr;
        rr.SetSenderSsrc(ssrc_);
        rr.SetReportBlocks(CreateRtcpReportBlocks(ctx.feedback_state_));
        sender.AppendPacket(rr);
    }
    void RTCPSender::BuildNack(const RtcpContext &ctx, PacketSender &sender)
    {
        webrtc::rtcp::Nack nack;
        nack.SetSenderSsrc(ssrc_);
        nack.SetMediaSsrc(remote_ssrc_);
        nack.SetPacketIds(ctx.nack_list_, ctx.nack_size_);
        sender.AppendPacket(nack);
    }
    void RTCPSender::BuildPli(const RtcpContext &ctx, PacketSender &sender)
    {
        webrtc::rtcp::Pli pli;
        pli.SetSenderSsrc(ssrc_);
        pli.SetMediaSsrc(remote_ssrc_);
        sender.AppendPacket(pli);
    }
    void RTCPSender::SetSrInfo(uint32_t rtp_timestamp, webrtc::NtpTime ntp)
    {
        lastest_rtp_timestamp_ = rtp_timestamp;
        lastest_ntp_ = ntp;
    }
    void RTCPSender::BuildXR(const RtcpContext &ctx, PacketSender &sender)
    {

        if (audio_)
        {
            return;
        }
        webrtc::rtcp::ExtendedReports xr;
        xr.SetSenderSsrc(ssrc_);
        if (!sending_ && enable_xr_)
        {
            webrtc::rtcp::Rrtr rrtr;
            rrtr.SetNtp(clock_->ConvertTimestampToNtpTime(ctx.now_));
            xr.SetRrtr(rrtr);
        }
        sender.AppendPacket(xr);
    }

} // namespace xrtc